/**
 * 项目名：	Rumba Plan
 * 
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2006，所有权利保留。
 * 
 * 文件名：	BackProcess.java
 * 模块说明：	
 * 修改历史：
 * 2006-10-27 - lxm - 创建。
 */
package com.hd123.rumba.runtime.backprocess;

import java.util.Date;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;

import com.hd123.rumba.domain.IDomain;
import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.log.RumbaLogger;
import com.hd123.rumba.prompt.CPrompt;
import com.hd123.rumba.prompt.IPrompt;
import com.hd123.rumba.prompt.IPromptMgr;
import com.hd123.rumba.runtime.HibernateSession;
import com.hd123.rumba.runtime.NamingMgr;
import com.hd123.rumba.runtime.OperateContext;
import com.hd123.rumba.user.IRole;
import com.hd123.rumba.user.IUser;
import com.hd123.rumba.util.StringUtil;

/**
 * 后台处理过程
 * <p>
 * 
 * 设计作为所有后台处理过程的基类。
 * <p>
 * 
 * 尽管BackProcess在大多数情况下表现得像Thread，但是派生类一般情况下不要重写run()，取而代之的是execute()。
 * <p>
 * 
 * 对Thread进行了扩展，其中包含了以下功能：<br>
 * <li>包含了对大部分情况下异常的处理，写日志。</li>
 * <li>处理结束后关闭可能使用的HibernateSession。</li>
 * <li>处理结束后将运行结果产生一条提示(Prompt)信息。</li
 * 
 * @author lxm
 * 
 */
public abstract class BackProcess extends Thread {

  private String uuid;
  private IDomain domain;
  private String[] ownerKeys = new String[]{};
  private ProgressStack progressStack;
  private Date startTime = new Date();
  private IUser operator;
  private TaskNature nature;

  // 默认情况下的处理进度栈。
  private static final ThreadLocal<ProgressStack> progressStack2 = new ThreadLocal();

  public BackProcess() {
    super();
    RumbaLogger logger = RumbaLogger.getLogger(this.getClass());
    try {
      this.uuid = StringUtil.createUuid();
    } catch (Exception e) {
      logger.error("创建后台处理过程时发生错误。", e);
    }
  }

  /**
   * 取得当前的后端处理过程（线程）对象。
   * 
   * @return 返回取得的后端处理过程（线程）对象。当当前线程不是BackProgress的时候返回null。
   */
  public synchronized static BackProcess getProcess() {
    Thread thd = Thread.currentThread();
    if (thd instanceof BackProcess)
      return (BackProcess) thd;
    else
      return null;
  }

  /**
   * 取得当前线程对应的处理进度栈。
   * 
   * @return 返回取得的处理进度栈对象。一般情况下取得的是{@link #getProgressStack()}。但当出现当前线程不是
   *         BackProgress，或者{@link #getProgressStack()}为null等情况，将返回一个默认的ProgressStack，
   *         而不是null。这样的设计是为了简化调用者的处理。
   */
  public synchronized static ProgressStack getProgressStack2() {
    ProgressStack ps = null;
    try {
      BackProcess process = BackProcess.getProcess();
      ps = process.getProgressStack();
    } catch (Exception e) {
    }
    if (ps == null) {
      ps = progressStack2.get();
      if (ps == null) {
        ps = new ProgressStack();
        ps.pushNew(1);
        progressStack2.set(ps);
      }
    }
    return ps;
  }

  /**
   * 取得当前线程中的当前处理进度对象。
   * 
   * @return 返回取得的处理进度对象。无论在何种情况下都不会返回null，详细情况参见{@link #getProgressStack2()}的
   *         有关说明。
   */
  public synchronized static Progress getProgress() {
    try {
      return BackProcess.getProgressStack2().peek();
    } catch (Exception e) {
      return null;
    }
  }

  /**
   * 取得处理过程的标题文字。<br>
   * 将被应用于例如进程的显示等地方。
   * 
   * @return
   */
  public abstract String getCaption();

  /**
   * 取得提示信息的接收人，为IUser或IRole。
   * 
   * @return
   */
  public abstract Object getPromptReceiver();

  /**
   * 取得提醒的最小提示级别。取值为“{#link CPrompt}.VALUE_LEVEL_*”常量。
   * <p>
   * 
   * 默认取值为最小级别{@link CPrompt#VALUE_LEVEL_NORMAL}，即任何情况下都会发出一个提醒。派生类可以通过重写的方法指定其为更高的级别。
   * 
   * @return
   */
  public Integer getPromptMinimumLevel() {
    return CPrompt.VALUE_LEVEL_NORMAL;
  }

  /**
   * 替代run()，派生类应通过重写该方法。其中为线程的执行程序。
   * 
   */
  public abstract void execute() throws Exception;

  public synchronized void start() {
    super.start();
    this.startTime = new Date();
  }

  public final void run() {
    try {
      Thread.sleep(100);
    } catch (InterruptedException e) {
    }

    RumbaLogger logger = RumbaLogger.getLogger(this.getClass());
    Integer promptLevel = CPrompt.VALUE_LEVEL_NORMAL;
    String message = "";
    try {

      this.execute();

      message = "执行成功！";
      logger.info(message);
    } catch (InterruptedException e) {
      message = "被用户中断！";
      logger.warn(message, e);
      promptLevel = CPrompt.VALUE_LEVEL_HIGHER;
    } catch (Exception e) {
      message = "执行失败！";
      logger.error(message, e);
      promptLevel = CPrompt.VALUE_LEVEL_HIGHEST;
    } finally {
      try {
        if (promptLevel.compareTo(this.getPromptMinimumLevel()) >= 0)
          this.submitPromptT(promptLevel, this.getCaption() + ": " + message);
      } catch (Exception e) {
        logger.error("后端处理无法提交提示信息。", e);
      }
      try {
        HibernateSession.closeSession();
      } catch (HibernateException e) {
      }
      BackProcessPool.getInstance().release(this);
    }
  }

  public synchronized String getUuid() {
    return uuid;
  }

  public synchronized String[] getOwnerKeys() {
    return ownerKeys;
  }

  public synchronized void setOwnerKeys(String[] ownerKeys) {
    this.ownerKeys = ownerKeys;
  }

  public synchronized ProgressStack getProgressStack() {
    return progressStack;
  }

  synchronized void setProgressStack(ProgressStack progress) {
    this.progressStack = progress;
  }

  public synchronized IDomain getDomain() {
    return domain;
  }

  public synchronized void setDomain(IDomain domain) {
    this.domain = domain;
  }

  public synchronized IUser getOperator() {
    return operator;
  }

  public synchronized void setOperator(IUser operator) {
    this.operator = operator;
  }

  public Date getStartTime() {
    return startTime;
  }

  /**
   * 将执行结果提交提示给指定接收方。<br>
   * 独立事务。<br>
   * 
   * @param receiver
   *          指定接收提示的接收方。允许的接收方必须为IUser或IRole。
   * @param level
   *          指定提示级别。取值参见CPrompt.VALUE_LEVEL_*。
   * @param message
   *          提示信息。
   * @param operator
   * @throws BusinessException
   * @throws HibernateException
   */
  protected void submitPromptT(Integer level, String message) throws BusinessException,
      HibernateException {
    if (level == null)
      throw new BusinessException("必须的参数level不可为空。");
    Object receiver = this.getPromptReceiver();
    if (receiver == null)
      throw new BusinessException("必须的参数promptReceiver不可为空。");
    IUser user = receiver instanceof IUser ? (IUser) receiver : null;
    IRole role = receiver instanceof IRole ? (IRole) receiver : null;
    if (user == null && role == null)
      throw new BusinessException("指定参数promptReceiver的类型不合法。");

    IPromptMgr promptMgr = (IPromptMgr) NamingMgr.getInstance().lookupMgr(IPromptMgr.class);
    IPrompt prompt = promptMgr.createPrompt(domain);
    if (user != null)
      prompt.setReceiverUser(user);
    if (role != null)
      prompt.setReceiverRole(role);
    prompt.setSenderId(this.uuid);
    prompt.setLevel(level);
    prompt.setMessage(message);
    prompt.setLink("log/ViewProcessLog?start=view&process_uuid=" + this.uuid);

    Session ses = HibernateSession.sessionFactory.openSession();
    Transaction tx = ses.beginTransaction();
    try {
      promptMgr.save(ses, prompt, new OperateContext(operator));
      tx.commit();
    } catch (Exception e) {
      tx.rollback();
      throw new BusinessException(e.getMessage());
    } finally {
      ses.flush();
      ses.close();
    }
  }

  public synchronized TaskNature getNature() {
    return nature;
  }

  public synchronized void setNature(TaskNature nature) {
    this.nature = nature;
  }

}
